/*
 * Copyright © 2012 Linaro Limited
 *
 * This file is part of glmark2.
 *
 * glmark2 is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * glmark2 is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with glmark2.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors:
 *  Simon Que <sque@chromium.org>
 */

#define __STDC_FORMAT_MACROS

#include <inttypes.h>
#include <stdio.h>
#include <sys/time.h>

#include "timer.h"
#include "util.h"

Timer::Timer(int max_cycles, const std::string& name) {
    data_.resize(max_cycles);

    timestamp_count_ = 0;
    cycle_count_ = 0;
    max_cycles_ = max_cycles;
    current_cycle_data_ = &data_[0];

    timer_list[name] = this;
    name_ = name;
}

Timer::~Timer() {
    timer_list.erase(name_);
}

void Timer::set_max_timestamps(int max_timestamps) {
    max_timestamps_ = max_timestamps;
    for (std::vector<std::vector<uint64_t> >::iterator iter = data_.begin();
         iter != data_.end(); ++iter) {
        (*iter).resize(max_timestamps);
    }
}

void Timer::next_cycle() {
    // However many timestamps have been recorded on the first cycle is the max
    // number of timestamps allowed on subsequent cycles.
    if (cycle_count_ == 0) {
        set_max_timestamps(timestamp_count_);
    } else if (timestamp_count_ < max_timestamps_) {
        for (int i = timestamp_count_; i < max_timestamps_; i += 1)
          (*current_cycle_data_)[i] =
              (*current_cycle_data_)[timestamp_count_ - 1];
    }

    if (cycle_count_ < max_cycles_) {
        cycle_count_ += 1;
        timestamp_count_ = 0;
        current_cycle_data_ = &data_[cycle_count_];
    }
}

void Timer::read_timestamp(int id) {
    // Do not read a timestamp if we've exceeded the max number of timestamps
    // (established by number of calls on cycle 0) or the max number of cycles.
    if (timestamp_count_ >= max_timestamps_ && cycle_count_ > 0)
        return;
    if (cycle_count_ >= max_cycles_)
        return;

    // First cycle has no limit to timestamps.
    // Subsequent cycles must not exceed that number of timestamps.
    if (cycle_count_ == 0) {
        timestamp_ids_.push_back(id);
        data_[0].push_back(Util::get_timestamp_us());
    } else {
        (*current_cycle_data_)[timestamp_count_] = Util::get_timestamp_us();
    }
    timestamp_count_ += 1;
}

void Timer::print_results() {
    unsigned int i;
    int j;
    for (i = 0; i < timestamp_ids_.size() - 1; i += 1)
        printf("\t%d", timestamp_ids_[i]);
    printf("\n");
    for (i = 0; i < timestamp_ids_.size(); i += 1)
        printf("========");
    printf("\n");
    for (j = 0; j < cycle_count_; j += 1) {
        std::vector<uint64_t>& current = data_[j];

        if (j > 0)
          printf("%" PRIi64 "", current[0] - data_[j-1][max_timestamps_-1]);
        for(int i = 0; i < max_timestamps_ - 1; i++) {
            printf("\t%" PRIi64 "", current[i + 1] - current[i]);
        }
        printf("\n");
    }
}

Timer* Timer::get_timer(const std::string& name) {
    std::map<std::string, Timer*>::const_iterator iter = timer_list.find(name);
    if (iter == timer_list.end())
        return NULL;
    return iter->second;
}

std::map<std::string, Timer*> Timer::timer_list;
